package com.ideal.tools.ssh.executor;

import com.ideal.tools.ssh.entity.ExeCommand;
import com.ideal.tools.ssh.entity.SSHAuthor;
import com.ideal.tools.ssh.operation.LinuxOperation;
import com.ideal.tools.ssh.result.ExecutorResult;
import com.ideal.tools.ssh.result.LinuxResult;
import net.schmizz.keepalive.KeepAliveProvider;
import net.schmizz.sshj.DefaultConfig;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.transport.TransportException;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.xfer.FileSystemFile;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * Created by CC on 2016/2/29.
 *
 * 以ssh的方式与linux交互
 *
 * 一个执行器持有 一个长连接
 * 调用exec方法是新起一个会话
 *
 */
public class SSHExecutor implements Executor{

    final SSHClient ssh;

    SSHAuthor sshAuthor;
    private static Logger logger = LoggerFactory.getLogger(SSHExecutor.class);
    public SSHExecutor(){
        //初始化一个长连接ssh
        DefaultConfig defaultConfig = new DefaultConfig();
        defaultConfig.setKeepAliveProvider(KeepAliveProvider.KEEP_ALIVE);
        ssh = new SSHClient(defaultConfig);
    }

    public LinuxResult initSSHConnect()  {
        LinuxResult linuxResult = null;
        logger.debug("###sshExecutor begin sshAuthor value:"+sshAuthor);
        if(sshAuthor==null){
            String exceptionStr= "The SSHAuthor is null!please init login in info!";
            linuxResult = initFaultResult(exceptionStr,exceptionStr);
            return linuxResult;
        }
        try {
            ssh.loadKnownHosts();
            //添加公钥
            String pubToken=sshAuthor.getPubToken();
            if(pubToken!=null&& !StringUtils.isBlank(pubToken)){
                ssh.addHostKeyVerifier(pubToken);
            }
            ssh.addHostKeyVerifier(new PromiscuousVerifier());

            ssh.connect(sshAuthor.getHost());
            ssh.getConnection().getKeepAlive().setKeepAliveInterval(5); //every 60sec
            ssh.authPassword(sshAuthor.getUsername(),sshAuthor.getPasswd());


        } catch (IOException e) {
            String exceptionStr = e.getMessage();
            linuxResult = initFaultResult(exceptionStr,"connect to host["+sshAuthor.getHost()+"] faild!");
            //手动强制关闭
            ssh.getConnection().getKeepAlive().interrupt();
            closeConnection();
//            e.printStackTrace();
            logger.debug("###error:"+e);
            return linuxResult;
        }
        linuxResult=initSuccResult("USER["+sshAuthor.getUsername()+"] login in HOST["+sshAuthor.getHost()+"] SUCCESS!");
        return linuxResult;
    }

    public SSHAuthor getSshAuthor() {
        return sshAuthor;
    }

    public void setSshAuthor(SSHAuthor sshAuthor) {
        this.sshAuthor = sshAuthor;
    }


    @Override
    public ExecutorResult exec(ExeCommand exeCommand) {
        LinuxResult linuxResult=new LinuxResult(this);
        Integer exitCode;
        String errOut;
        String stdOut;
        Session session = null;
        try {
//            if(exeCommand.getCommandOperationType().equals(LinuxOperation.OperationType.EXE_CMD)) {
                session = ssh.startSession();
                String cmd = exeCommand.getCommand();

                Session.Command command = session.exec(cmd);


                stdOut = IOUtils.readFully(command.getInputStream()).toString();
                errOut = IOUtils.readFully(command.getErrorStream()).toString();
                exitCode = command.getExitStatus();
                //这个地方 最多等待10秒 如果10秒针到了 就直接把exitcode 设置为成功
                //所有在前台调用 放回结果的时候 其实exitcode　是有可能不太准确的
                int count = 0;
                while (exitCode == null) {
                    if (count == 10) {
                        exitCode = 0;
                        break;
                    }
                    //等待10秒钟
                    command.join(1, TimeUnit.SECONDS);
                    exitCode = command.getExitStatus();
                    count++;
                }

                command.close();

                linuxResult.setErrOut(errOut);
                linuxResult.setStdOut(stdOut);
                linuxResult.setExitCode(exitCode);
//            }else if(exeCommand.getCommandOperationType().equals(LinuxOperation.OperationType.SCP_DOWNLOAD)){
//                ssh.newSCPFileTransfer().download(exeCommand.getFromPath(),
//                        new FileSystemFile(exeCommand.getToPath()));
//
//            }


        } catch (Exception e) {
            e.printStackTrace();
            linuxResult.setException(e.getMessage());
        } finally {
            //放入执行命令
            linuxResult.setCmd(exeCommand.getCommand());
            try {
                session.close();
            } catch (TransportException e) {
                e.printStackTrace();
            } catch (ConnectionException e) {
                e.printStackTrace();
            }
        }

        return linuxResult;
    }

    /**
     * 关闭连接
     */
    public void closeConnection(){
        try {
            if(ssh!=null) {
//                if(ssh.getConnection() !=null){
//                    ssh.getConnection().getKeepAlive().interrupt();
//                }
                ssh.disconnect();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public LinuxResult initFaultResult(String exception,String note){
        LinuxResult linuxResult = new LinuxResult(null,null,LinuxResult.DEFAULT_FAILD_CODE);
        linuxResult.setException(exception);
        linuxResult.setNote(note);
        linuxResult.setSshExecutor(this);
        return linuxResult;
    }

    public LinuxResult initSuccResult(String note){
        LinuxResult linuxResult = new LinuxResult(null,null,LinuxResult.DEFAULT_SUCCESS_CODE);
        linuxResult.setNote(note);
        linuxResult.setSshExecutor(this);
        return linuxResult;
    }

}
